﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


using SpiritEngine.Source.ApplicationLayer.Services;
using SpiritEngine.Source.EngineCore;
using SpiritEngine.Source.Utilities;


namespace SpiritEngine.Source.ApplicationLayer
{
    #region Globals

    #endregion


    public class ServicesManager: SE_Base, ISE_ServicesManager
    {
        #region Constants/Enums

        #endregion


        #region Structs

        #endregion


        #region Static Member Variables and Methods

        #endregion


        #region Member Variables

        private List<ISE_Service> m_ServicesList;


        // ========================================================================================================================================================================================================
        // Property Variables
        // ========================================================================================================================================================================================================

        // These are private to force classes that inherit from this class to declare their own member variable for use by the corresponding properties.
        // This way, for example, we can keep track of initialization state for each step in the inheritence chain.  So if the base class is initialized, but
        // the subclass is not, then the base class's IsInitialized property would return true, but the subclass's IsInitialized property would return false.
        private int m_SEObjectFlags = 0;    // Stores object flags defined by the SEObject_Flags enum.


        #endregion


        #region Events/Delegates

        #endregion


        #region Constructors and Destructors (aka Finalizers)

        public ServicesManager(ISE_EngineBase parent)
        {
            if (parent == null)
                throw new ArgumentException("ServicesManager(ISE_EngineBase): The parent parameter cannot be null!");


            m_Parent = parent;



            m_ServicesList = new List<ISE_Service>();
        }

        #endregion


        #region Non-Public Methods

        // This section is for methods that are declared as private, protected, protected internal, or internal.

        #endregion


        #region Public Methods

        /// <summary>
        /// This function adds a new service into the ServicesManager, providing that it does not already contain
        /// a service of that type.
        /// </summary>
        /// <param name="newService">The new service to add.</param>
        public void AddService(ISE_Service newService)
        {
            if (newService == null)
            {
                throw new ArgumentException("ServicesManager.Add(ISE_Service): The passed in service is null!");
            }

            if (this.ContainsService(newService.GetType()))
            {
                throw new ArgumentException("ServicesManager.Add(ISE_Service): A service of this type has already been added!");
            }


            m_ServicesList.Add(newService);
        }


        /// <summary>
        /// Clears the services list and disposes of all services.
        /// </summary>
        public void ClearAllServices()
        {
            foreach (ISE_Service service in m_ServicesList)
            {
                service.Dispose();
            }


            m_ServicesList.Clear();
        }


        /// <summary>
        /// This function checks if the ServicesManager contains a service of the specified type or not.
        /// </summary>
        /// <param name="serviceType">The type of service to check for.</param>
        /// <returns>True if a service of the specified type is found, or false otherwise.</returns>
        public bool ContainsService(Type serviceType)
        {
            if (serviceType == null)
            {
                throw new ArgumentException("ServicesManager.Contains(Type): The passed in service type is null!");
            }

            if (!typeof(ISE_Service).IsAssignableFrom(serviceType))
            {
                throw new ArgumentException("ServicesManager.ContainsService(Type): The passed in type is not an ISE_Service type!");
            }


            bool result = false;


            foreach (ISE_Service s in m_ServicesList)
            {
                if (serviceType.IsAssignableFrom(s.GetType()))
                {
                    result = true;
                    break;
                }
                
            }
            

            return result;
        }


        /// <summary>
        /// This function gets the specified service.
        /// </summary>
        /// <param name="serviceType">The type of service to get.</param>
        /// <returns>The requested service, or null if the requested service is not found.</returns>
        public ISE_Service GetService(Type serviceType)
        {
            if (serviceType == null)
            {
                throw new ArgumentException("ServicesManager.GetService(Type): The passed in service type is null!");
            }

            if (!typeof(ISE_Service).IsAssignableFrom(serviceType))
            {
                throw new ArgumentException("ServicesManager.GetService(Type): The passed in type is not an ISE_Service type!");
            }


            foreach (ISE_Service s in m_ServicesList)
            {

                if (serviceType.IsAssignableFrom(s.GetType()))
                {
                    return s;
                }

            }


            return null;
        }


        /// <summary>
        /// Initializes this object.
        /// </summary>
        /// <param name="paramsList">A variable length parameter list.
        /// 
        /// EXPECTED PARAMETERS:
        /// None</param>
        /// <returns>True if successful or false otherwise.</returns>
        public override bool Initialize(params object[] paramsList)
        {
            base.Initialize();


#if VERIFY_PARAMS
            if (!SE_Debug.VerifyParameters(paramsList))
            {
                throw new ArgumentException("ServicesManager.Initialize(object[]): Invalid params!");
            }
#endif


            // Set the initialized flag to indicate that this object has been initialized.
            m_SEObjectFlags = SE_Misc.Flags_SetFlags(m_SEObjectFlags, (int) SEObject_Flags.Initialized);

            return true;
        }


        /// <summary>
        /// This function replaces the service of the specified type with the passed in service.
        /// The old service is returned so it can be saved if desired, or null is returned if no
        /// service of the specified type is found (and the new service is still added into the ServicesManager).
        /// </summary>
        /// <param name="serviceType">The type of service to replace.</param>
        /// <param name="newService">The service to replace the old service of its type.</param>
        /// <returns>The old service or null if no old service of the type was found.</returns>
        public ISE_Service ReplaceService(Type serviceType, ISE_Service newService)
        {
            if (serviceType == null)
            {
                throw new ArgumentException("ServicesManager.ReplaceService(Type, ISE_Service): The passed in service type is null!");
            }

            if (! typeof(ISE_Service).IsAssignableFrom(serviceType))
            {
                throw new ArgumentException("ServicesManager.ReplaceService(Type, ISE_Service): The passed in type is not an ISE_Service type!");
            }

            if (newService == null)
            {
                throw new ArgumentException("ServicesManager.ReplaceService(Type, ISE_Service): The passed in service is null!");
            }

            //System.Diagnostics.Debug.WriteLine("TYPE: " + serviceType);
            //System.Diagnostics.Debug.WriteLine("SERVICETYPE: " + newService.GetType());

            // newService must be the same type as the specified type.
            if (!(serviceType.IsAssignableFrom(newService.GetType())))
            {
                throw new ArgumentException("ServicesManager.ReplaceService(Type, ISE_Service): The passed in service is not of the specified type!");
            }



            ISE_Service result = null;
            
            for (int i = 0; i < m_ServicesList.Count; i++)
            {

                if (serviceType.IsAssignableFrom(m_ServicesList[i].GetType()))
                {
                    result = m_ServicesList[i];
                    
                    m_ServicesList.RemoveAt(i);

                    AddService(newService);

                    break;
                }

            }


            // If we didn't find a service of the specified type, still add the new service.
            if (result == null)
                AddService(newService);


            return result;
        }


        /// <summary>
        /// This function removes the service of the specified type from the ServicesManager and returns it.
        /// If no service of the specified type is found, it returns null.
        /// </summary>
        /// <param name="newService">The type of service to remove.</param>
        /// <returns>The removed service or null if there is no service of the specified type.</returns>
        public ISE_Service RemoveService(Type serviceType)
        {
            if (serviceType == null)
            {
                throw new ArgumentException("ServicesManager.RemoveService(Type): The passed in service type is null!");
            }

            if (!typeof(ISE_Service).IsAssignableFrom(serviceType))
            {
                throw new ArgumentException("ServicesManager.ReplaceService(Type, ISE_Service): The passed in type is not an ISE_Service type!");
            }


            ISE_Service result = null;


            for (int i = 0; i < m_ServicesList.Count; i++)
            {

                if (serviceType.IsAssignableFrom(m_ServicesList[i].GetType()))
                {
                    result = m_ServicesList[i];

                    m_ServicesList.RemoveAt(i);

                    break;
                }

            }


            return result;
        }

        #endregion


        #region Interface Methods

        // This section is for methods that are part of the interfaces that the class implements.

        #region IDisposable Methods

#if USE_IDISPOSABLE

        /// <summary>
        /// This function handles the actual "disposing" of this object.
        /// ANY classes that are inherited from this class, no matter how distantly, MUST override this method so that they can perform their own cleanup!
        /// </summary>
        /// <param name="disposing">This parameter will be true if this function is being called by the public Dispose() method, or false otherwise.</param>
        protected override void Dispose(bool disposing)
        {
            // Check if this object has already been disposed.
            if (SE_Misc.Flags_HasFlags(m_SEObjectFlags, (int)SEObject_Flags.Disposed))
                return;



            if (disposing)
            {
                /*
                * The following text is from MSDN  (http://msdn.microsoft.com/en-us/library/fs2xkftw%28VS.80%29.aspx)
                * 
                * 
                * Dispose(bool disposing) executes in two distinct scenarios:
                * 
                * If disposing equals true, the method has been called directly or indirectly by a user's code and managed and unmanaged resources can be disposed.
                * If disposing equals false, the method has been called by the runtime from inside the finalizer and only unmanaged resources can be disposed. 
                * 
                * When an object is executing its finalization code, it should not reference other objects, because finalizers do not execute in any particular order. 
                * If an executing finalizer references another object that has already been finalized, the executing finalizer will fail.
                */


                // Unregister Events


                // Clean up all managed resources.
                foreach (ISE_Service service in m_ServicesList)
                {
                    service.Dispose();
                }


            }

            // Clean up all unmanaged resources.



            // Call the base class's Dispose() method if it has one.
            base.Dispose(disposing);
        }

#endif

        #endregion

        #endregion


        #region Event Invocation Methods

        // This section is for methods that invoke the events of this class.  For example:
        //
        // Invoke the Changed event; called whenever list changes
        // protected virtual void OnChanged(EventArgs e) 
        // {
        //		if (Changed != null)
        //			Changed(this, e);
        // }

        #endregion


        #region Operators

        #endregion


        #region Debug Methods

        // This will compile the debug code only if the DEBUG symbol is defined.
        // To define this symbol for all source files in the project, open the Project Properties,
        // and go to the Build tab.  Check the "Define DEBUG constant" checkbox.  With this box checked,
        // the compiler will automatically define the symbol DEBUG.
#if (DEBUG)
        // put all debug methods in here...
#endif

        #endregion


        #region Properties

        /// <summary>
        /// Provides access to the object's "Spirit Engine Object" flags.
        /// </summary>
        public override int SEObjectFlags
        {
            get
            {
                return m_SEObjectFlags;
            }
            protected set // Can only be set from within this class and subclasses
            {
                m_SEObjectFlags = value;
            }
        }


        /// <summary>
        /// Returns the number of services that are currently in the ServicesManager.
        /// </summary>
        public int ServiceCount
        {
            get
            {
                return m_ServicesList.Count();
            }
        }

        #endregion

    }
}
